package cn.trigram.lang.ary;

import java.math.BigInteger;

/**
 * 原码(Sign-Magnitude)、反码(ones' Complement)、补码(two's Complement)表示及二进制计算。<br/>
 * 首先，Java的所有数据表示都是补码形式。相关补码的知识参考计算机组成原理及同余定理、模的知识。<br/> 这里只讲一下怎么求负数补码（正数的原码、反码、补码都是一个）的两种方法： <br/>
 * 1、取巧法：原码 取反得到 反码，反码 加一得到 补码 <br/> 2、本质法： 假设求解一个字节的负数补码，以-128为例 <br/>
 * ①、一个字节可表达的数字范围是2的8次方，二进制表示：0b1_0000_0000，作为被减数。<br/> ②、-128 取绝对值 128，注意这里用8位的无符号二进制表示，即为：0b1000_0000，作为减数。<br/>
 * ③、此时（被减数-减数）即等于了-128的有符号补码。<br/>
 */
public class BinaryUtil {

  public static final byte B_ZERO = 0b0000_0000;

  public static final byte B_ONE = 0b0000_0001;

  public static final short S_ZERO = 0;

  public static final short S_ONE = 1;

  public static final int I_ZERO = 0;

  public static final int I_ONE = 1;

  public static final long L_ZERO = 0L;

  public static final long L_ONE = 1L;

  public static final char[] BINARY_CHARS = {'0', '1'};

  public static char[] twoComplement(byte[] bs) {

    char[] chs = new char[8 * bs.length];
    for (int i = 0; i < bs.length; i++) {
      byte n = bs[i];
      for (int j = 7; j >= 0; j--) {
        byte t = n;
        t >>= j;
        t &= B_ONE;
        chs[i * 8 + (7 - j)] = BINARY_CHARS[t];
      }
    }
    return chs;
  }

  public static char[] twoComplement(byte n) {

    char[] chs = new char[8];
    char oneChar = '1', zeroChar = '0';
    for (int i = 0; i < 8; i++) {
      byte t = n;
      t &= B_ONE;
      chs[7 - i] = t == B_ZERO ? zeroChar : oneChar;
      n >>= 1;
    }
    return chs;
  }

  public static char[] twoComplement(int n) {

    char[] chs = new char[32];
    for (int i = 0; i < 31; i++) {
      int t = n;
      t &= I_ONE;
      chs[31 - i] = BINARY_CHARS[t];
      n >>= 1;
    }
    return chs;
  }

  public static char[] twoComplement(short n) {

    char[] chs = new char[16];
    char oneChar = '1', zeroChar = '0';
    for (int i = 0; i < 15; i++) {
      int t = n;
      t &= S_ONE;
      chs[15 - i] = t == S_ZERO ? zeroChar : oneChar;
      n >>= 1;
    }
    return chs;
  }

  public static char[] twoComplement(long n) {

    char[] chs = new char[64];
    char oneChar = '1', zeroChar = '0';
    for (int i = 0; i < 63; i++) {
      long t = n;
      t &= L_ONE;
      chs[63 - i] = t == L_ZERO ? zeroChar : oneChar;
      n >>= 1;
    }
    return chs;
  }

  public static char[] twoComplement(BigInteger bigInteger) {

    char[] chars = bigInteger.toString(2).toCharArray();
    if (chars[0] == '-') {
      chars[0] = '1';
    }
    return chars;
  }

}
